
package org.sidora.core.context;

import java.lang.RuntimeException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeSet;

import javax.swing.ImageIcon;

//import strucmod.grafiques.ColorItem;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.query.Predicate;

import org.sidora.core.Action;
import org.sidora.core.Category;
import org.sidora.core.Dating;
import org.sidora.core.Describable;
import org.sidora.core.Feature;
import org.sidora.core.KeyObject;
import org.sidora.core.Sector;
import org.sidora.core.Site;
import org.sidora.core.Zone;
import org.sidora.core.util.AbstractInventory;
import org.sidora.core.util.OCProvider;
import org.sidora.core.util.ParameterCollector;

/**
 * Classe abstracta que representa una unitat estratigrafica
 * @author Enric Tartera, Juan Manuel Gimeno, Anna Arnalot, Roger Masgoret
 * @version 1.0
 */
public class Context implements Comparable, Cloneable, Describable{
    
    protected KeyObject id;
    
    //private Assignment assignment;
    
    private TreeSet <Action> actions; //= new TreeSet<Action>(); //S'ha de triar la campaña
    private TreeSet <Sector> sectors; //= new TreeSet<Sector>();
    
    private Feature feature;
    
    private String description;
    private String interpretation;
    
    private TreeSet<StratigraphicRelation> relations;

    
    private boolean hasMaterialRemains = false;
    private boolean hasSamples = false;
    private boolean isFinished = false;
    
    public Category interpretedType;
    
    public Category locationInSector;
    
    public TreeSet<Category> takenMaterialRemains;
    public TreeSet<Category> diggingMethod;
    public TreeSet<Category> intrusionRisk;
    
    public Dating dating;
    
    Context(){
        setActions(new TreeSet<Action>());
        sectors = new TreeSet<Sector>();
        relations = new TreeSet<StratigraphicRelation>();
        dating = new Dating();
    }
    
    
    public ImageIcon getIcon() {
        return new javax.swing.ImageIcon(getClass().getResource("/org/sidora/core/context/resources/context32.png"));
    }
    
    public String getDescription() {
        return description;
    }
    
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    
    public String getInterpretation() {
        return interpretation;
    }
    
    
    public void setInterpretation(String interpretation) {
        this.interpretation = interpretation;
    }
    
    
    public boolean isFinished() {
        return this.isFinished;
    }
    
    public void setFinished(boolean finished) {
        this.isFinished = finished;
    }
    
    
    public void addMaterialTaked(Category category) {
        takenMaterialRemains.add(category);
    }
    public void removeMaterialTaked(Category category) {
        takenMaterialRemains.remove(category);
    }
    
    public TreeSet<Category> getTakenMaterialRemains() {
        return takenMaterialRemains;
    }
    
    public void setTakenMaterialRemains(TreeSet<Category> takenMaterial) {
        this.takenMaterialRemains = takenMaterial;
    }
    
    public TreeSet<Category> getIntrusionRisk() {
        return intrusionRisk;
    }
    public void setIntrusionRisk(TreeSet<Category> intrusionRisk) {
        this.intrusionRisk = intrusionRisk;
    }
    public TreeSet<Category> getDigginMethod() {
        return diggingMethod;
    }
    public void setDiggingMethod(TreeSet<Category> diggingMethod) {
        this.diggingMethod = diggingMethod;
    }
    public Dating getDating() {
        return dating;
    }
    public void setDating(Dating dating) {
        this.dating = dating;
    }
    
    
    public KeyObject getId() {
        return id;
    }
    public void setId(KeyObject id) {
        this.id = id;
    }
    public Category getLocationInSector() {
        return locationInSector;
    }
    public void setLocationInSector(Category locationInSector) {
        this.locationInSector = locationInSector;
    }
    public boolean hasMaterialRemains() {
        return hasMaterialRemains;
    }
    public void setHasMaterialReamins(boolean hasMaterialRemains) {
        this.hasMaterialRemains = hasMaterialRemains;
    }
    public boolean hasSamples() {
        return hasSamples;
    }
    public void setHasSamples(boolean hasSamples) {
        this.hasSamples = hasSamples;
    }
    
    
    
    public TreeSet<Sector> getSectors() {
        return sectors;
    }
    
    public String getSectorsString() {
        
        StringBuffer textbuffer = new StringBuffer("[");
        
        TreeSet set = this.getSectors();
        // boolean empty = set.isEmpty();
        Iterator iter = set.iterator();
        
        while (iter.hasNext()){
            Sector asector = (Sector) iter.next();
            textbuffer.append(asector.getIdSector().getZone().getIdZone().toString()
            +"."+asector.getIdSector().getNumber().toString());
            
            if (iter.hasNext())
                textbuffer.append("/");
        }
        textbuffer.append("]");
        return textbuffer.toString();
    }
    
    public String getFeatureString(){
        if (feature!=null){
            return feature.toSmallString();
        } else return "";
    }
    
    public void setSectors(TreeSet<Sector> sectors) {
//TODO repassar
//        if (feature!=null) {
//            feature.setSectors(sectors);
//        }
        this.sectors = sectors;
    }
    
    public Site getSite() {
        return id.getSite();
    }
    public void setSite(Site site) {
        this.id.setSite(site);
    }
    
    public Category getInterpretedType() {
        return interpretedType;
    }
    public void setInterpretedType(Category interpretedType) {
        this.interpretedType = interpretedType;
    }
    
    public Zone getZone() {
        return id.getZone();
    }
    public void setZone(Zone zone) {
        this.id.setZone(zone);
    }
    
    
    
    public static class Inventory extends AbstractInventory {
        
        public boolean exist(final KeyObject id) throws Exception{
            ObjectContainer db = OCProvider.getInstance();
            ObjectSet<Context> contexts = db.query(new Predicate<Context>() {
                
                public boolean match(Context candidate) {
                    return candidate.id.equals(id);
                }
            });
            db.commit();
            if (contexts.hasNext()){
                return true;
            } else {
                return false;
            }
        }
        
        @Override
        public Object getNewObject(ParameterCollector param) throws Exception {
            
            //Primer creem l'objecte segons el type
            Context context;
            //context = new Context();
            
            if(param.get("type")instanceof String){
                String type = (String) param.get("type");
                
                if (type.equals("Stratum")){
                    context = new Stratum();
                }else if (type.equals("Surface")){
                    context = new Surface();
                }else if (type.equals("Deposed")){
                    context = new DeposedElement();
                }else if (type.equals("Devastated")){
                    context = new DevastatedContext();
                }else if (type.equals("Builded")){
                    context = new BuildedContext();
                }else if (type.equals("Technical")){
                    context = new TechnicalContext();
                }else if (type.equals("Emptyed")){
                    context = new EmptyedContext();
                }else if (type.equals("Old")){
                    context = new Context();
                }else{
                    throw new RuntimeException("Type not found");
                }
            }else{
                throw new RuntimeException("Type not specified");
            }
            
            //Aconsgeguim el key object
            context.id = getNewKey(param);
            //En cas que es vulgui un altre identificador
            if (param.get("id")!=null){
                Integer num = (Integer) param.get("id");
                context.id.setNumber(num);
            }
            //En cas d'haver passat un Action l'afegim
            if (param.get("action") != null)
                context.getActions().add((Action) param.get("action"));
            
            //Afegim el fet o el sector
            if (param.get("feature") instanceof Feature){
                context.setFeature((Feature) param.get("feature"));
            }else if (param.get("sector") instanceof Sector){
                TreeSet<Sector> treeSet = new TreeSet();
                treeSet.add((Sector) param.get("sector"));
                context.setSectors(treeSet);
            }else{
                TreeSet<Sector> treeSet = new TreeSet();
                Iterator iter = Sector.Inventory.getList(context.id.getZone()).iterator();
                while (iter.hasNext()){
                    Sector sector = (Sector)iter.next();
                    if ((sector).getIdSector().getNumber()==0)
                        treeSet.add(sector);
                    break;
                }
                
                context.setSectors(treeSet);
                //throw new RuntimeException();
            }
            
            return context;
        }
        
        @Override
        public Object getObject(ParameterCollector param) {
//		Carrega el Context amb el corresponent keyobject
            final KeyObject key = (KeyObject) param.get("id");
            ObjectContainer db = OCProvider.getInstance();
            
            ObjectSet <Context> ues = db.query(new Predicate <Context>() {
                public boolean match(Context aue) {
                    return (aue.id.equals(key));
                }
            });
            db.commit();
            
            if (ues.hasNext()){
                return ues.next();
            } else throw new RuntimeException("UE inexistent");
        }
        
        
        public static List getList() {
            ObjectContainer db = OCProvider.getInstance();
            List<Context> list = db.query(Context.class);
            db.commit();
            return list;
        }
        
        @Override
        public KeyObject getNewKey(ParameterCollector param) {
            
            final Zone zone = (Zone) param.get("zone");
            //se li dona el valor inicial per defecte
            KeyObject key = new KeyObject();
            key.setNumber(1);
            key.setSite(zone.getSite());
            key.setZone(zone);
            
            //es busca a la base d'objectes totes les inst�ncies de Context de la zona
            ObjectContainer db = OCProvider.getInstance();
            List <Context>contexts = db.query(new Predicate <Context> (){
                @Override
                public boolean match(Context candidate) {
                    return candidate.id.getZone().equals(zone);
                }
            });
            //En cas que hi hagi d'altres contexts a la zona busca el nombre m�s alt i en suma un
            if (contexts.size()!= 0){
                TreeSet <Context> tree = new TreeSet<Context>(contexts);
                Context context = tree.last();
                Integer integer = context.id.getNumber()+1;
                key.setNumber(integer);
                return key;
            }
            //En cas contrari pasa el valor per defecte
            else return key;
            
        }
        
        public static List<Context> getList(final Site site) {
            ObjectContainer db = OCProvider.getInstance();
            List<Context> list = db.query(new Predicate<Context>(){
                
                @Override
                public boolean match(Context context) {
                    // TODO Auto-generated method stub
                    return context.id.getSite()==site;
                }});
                db.commit();
                return list;
        }
        
        public static List<Context> getList(final Zone zone) {
            ObjectContainer db = OCProvider.getInstance();
            List<Context> list = db.query(new Predicate<Context>(){
                
                @Override
                public boolean match(Context context) {
                    // TODO Auto-generated method stub
                    return context.id.getZone()==zone;
                }});
                db.commit();
                return list;
        }
        
        public static List<Context> getList(final Sector sector) {
            ObjectContainer db = OCProvider.getInstance();
            List<Context> list = db.query(new Predicate<Context>(){
                
                @Override
                public boolean match(Context context) {
                    // TODO Auto-generated method stub
                    //return context.sectors.contains(sector);
                    return context.getSectors().contains(sector);
                }});
                db.commit();
                return list;
        }
        
        public static List<Context> getList(final Feature feature) {
            ObjectContainer db = OCProvider.getInstance();
            List<Context> list = db.query(new Predicate<Context>(){
                
                @Override
                public boolean match(Context context) {
                    //TODO comprovar als testos unitaris que la comparaci� funcioni.
                    return context.getFeature()==feature;
                }});
                //TODO mirar per que fem commit
                db.commit();
                return list;
        }
        
        public static TreeSet<Context> getListEqualTo(Context context){
            List<Equality> list = StratigraphicRelation.Inventory.getEqualTo(context);
            
            TreeSet<Context> treeset = new TreeSet();
            Iterator iter = list.iterator();
            while (iter.hasNext()){
                Equality equality = (Equality) iter.next();
                treeset.add(equality.getA());
                treeset.add(equality.getB());
            }
            
            treeset.remove(context);
            return treeset;
        }
        
        
        public static TreeSet<Context> getListEquivalentTo(Context context){
            List<Equivalence> list = StratigraphicRelation.Inventory.getEquivalentTo(context);
            
            TreeSet<Context> treeset = new TreeSet();
            Iterator iter = list.iterator();
            while (iter.hasNext()){
                Equivalence equivalence = (Equivalence) iter.next();
                treeset.add(equivalence.getA());
                treeset.add(equivalence.getB());
            }
            
            treeset.remove(context);
            return treeset;
        }
        
        public static TreeSet<Context> getListNextTo(Context context){
            List<Succesion> list = StratigraphicRelation.Inventory.getNextTo(context);
            
            TreeSet<Context> treeset = new TreeSet();
            Iterator iter = list.iterator();
            while (iter.hasNext()){
                Succesion succesion = (Succesion) iter.next();
                treeset.add(succesion.getA());
                treeset.add(succesion.getB());
            }
            
            treeset.remove(context);
            return treeset;
        }
        
        public static TreeSet<Context> getListPreviousTo(Context context){
            List<Succesion> list = StratigraphicRelation.Inventory.getPreviousTo(context);
            
            TreeSet<Context> treeset = new TreeSet();
            Iterator iter = list.iterator();
            while (iter.hasNext()){
                Succesion succesion = (Succesion) iter.next();
                treeset.add(succesion.getA());
                treeset.add(succesion.getB());
            }
            
            treeset.remove(context);
            return treeset;
        }
        
        public static List<Context> getList(String numbers, Site site){
            List <Context> list = new ArrayList<Context>();
            
            if (numbers.contains(",")||numbers.contains("/")||numbers.contains("=")){
                StringTokenizer token = null;
                if (numbers.contains(",")) token = new StringTokenizer(numbers, ",");
                if (numbers.contains("/")) token = new StringTokenizer(numbers, "/");
                if (numbers.contains("=")) token = new StringTokenizer(numbers, "=");
                while (token.hasMoreTokens()){
                    String astring = token.nextToken().trim();
                    
                    Context context = null;
                    try {
                        context = getContext(astring, Zone.Inventory.getZone(astring, site));
                    } catch (Exception ex) {
                        //return new ArrayList<Context>();
                    }
                    if (context!=null)list.add(context);
                }
                
            }else{
                Context context;
                try {
                    context = getContext(numbers.trim(), Zone.Inventory.getZone(numbers, site));
                } catch (Exception ex) {
                    return new ArrayList<Context>();
                }
                if (context!=null)list.add(context);
            }
            
            return list;
        }
        
        public static Context getContext(String string, final Zone zone) {
            
            Context context = null;
            
            final Integer number;
            try{
                number = new Integer(string.subSequence(string.length()-3, string.length()).toString());
            }catch (Exception e) {
                return null;
            }
            
            Iterator iter = zone.getContexts().iterator();
            while (iter.hasNext()){
                Context acontext = (Context) iter.next();
                if (acontext.getId().getNumber().equals(number)){
                    return acontext;
                }
            }
            
            return null;
        }
        
    }
    
//    static class Factory {// extends AbstractFactory{
//
//        protected static Object create(ParameterCollector param) {
//            // La política de creació de UE determina que només un usuari
//            // amb rol "admin" o "user" pot crear noves entitats.
//            // Per defecte es permet tamb? el password de root declarat com a variable est�tica de User
////	    		User user=null;
////	    		char[] password =null;
//
//            if(param.get("type")instanceof Category){
//                Category cat = (Category) param.get("type");
//                String type = cat.getValue();
//                if (type.equals("Stratum"))return new Stratum();
//                if (type.equals("Surface"))return new Surface();
//                if (type.equals("Deposed"))return new DeposedElement();
//                if (type.equals("Devastated"))return new DevastatedContext();
//                if (type.equals("Builded"))return new BuildedContext();
//                if (type.equals("Technical"))return new TechnicalContext();
//                if (type.equals("Emptyed"))return new EmptyedContext();
//
//            }else {
//                return new Context();
//            }
//            return null;
//        }
//    }
    
    
    public int compareTo(Object obj) {
        Context aue = (Context) obj;
        return this.toString().compareTo(aue.toString());
        
    }
    
    public boolean equals(Object obj) {
        if (obj == this)	return true;
        if (!(obj instanceof Context)) return false;
        Context context = null;
        try {
            context = (Context) obj;
        } catch (Exception e) {
            return false;
        }
        if (this.id != context.id) return false;
        return true;
    }
    
    
    public String toString() {
        NumberFormat form = NumberFormat.getInstance();
        form.setMinimumIntegerDigits(3);
        int numero = this.id.getNumber();
        StringBuilder str = new StringBuilder();
        str.append("UE: "+id.getZone().getIdZone().getNumber().toString()
        +"."+ form.format(numero));
        if (! getStringClass().equals("Context")){
            str.append(" "+getStringClass());
        }
        
        return str.toString();
    }
    public String toSmallString() {
        
        NumberFormat form = NumberFormat.getInstance();
        form.setMinimumIntegerDigits(3);
        int numero = this.id.getNumber();
        
        return id.getZone().getIdZone().getNumber().toString()
        +"."+ form.format(numero);
    }
    
    
    public Object clone() throws CloneNotSupportedException {
        Context clon = (Context) super.clone();
        return clon;
    }
    
    public Feature getFeature() {
        return feature;
    }
    
    public void setFeature(Feature feature) {
        this.feature = feature;
        //if (feature.getSectors()!=null) this.sectors = feature.getSectors();
    }
    
    public String getStringClass() {
        return "Context";
    }
    
    
    
    public String toNumString() {
        NumberFormat form = NumberFormat.getInstance();
        form.setMinimumIntegerDigits(3);
        int numero = this.id.getNumber();
        
        return id.getZone().getIdZone().getNumber().toString()
        +form.format(numero);
        
    }
    
    public TreeSet<Action> getActions() {
        return actions;
    }
    
    public void setActions(TreeSet<Action> actions) {
        this.actions = actions;
    }
    
    
    
    
}





